// AppMain.cpp
//
/////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "AppMain.h"

/////////////////////////////////////////////////////////////////////////////
// Global CApp object

CApp theApp;

/////////////////////////////////////////////////////////////////////////////
// Global functions
/////////////////////////////////////////////////////////////////////////////
//
// WinMain()
// The applications entry point.
//

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	if (!theApp.InitInstance(hInstance, nCmdShow, lpCmdLine)) 
		return FALSE;
	
	int nRet = theApp.Run();
	
	theApp.ExitInstance();
	
	return nRet;
}

//
// WndProc()
// Window procedure for the main window. Simply calls CApp::WindowProc.
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{	
	return theApp.WindowProc(hWnd, message, wParam, lParam);
}

/////////////////////////////////////////////////////////////////////////////
// CApp construction/destruction

CApp::CApp()
{
	m_hInst = 0;
	m_hWnd = NULL;
	m_hRC = NULL;

	m_hDll = NULL;

	m_pfGameInit = NULL;
	m_pfGameRender = NULL;
	m_pfGameMessage = NULL;
	m_pfGameFree = NULL;


	memset(m_szFolder, 0, sizeof(m_szFolder));
}

CApp::~CApp()
{
}

/////////////////////////////////////////////////////////////////////////////
// CApp member functions
/////////////////////////////////////////////////////////////////////////////
//#define FULLSCREEN
//
// InitInstance()
// Called when the application starts. Creates and shows the main window.
//
BOOL CApp::InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
{
	const char szWindowClass[] = "GameFrameWindowClass";
	const char szWindowTitle[] = "GameFrame";
	
	m_hInst = hInstance;
	
	// Register main window class
	WNDCLASSEX wcex;
	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = (WNDPROC)WndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_GameFrame);
	wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground = 0;
	wcex.lpszMenuName = NULL;
	wcex.lpszClassName = szWindowClass;
	wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);
	if(!RegisterClassEx(&wcex))
		return FALSE;

	TCHAR szPath[MAX_PATH];
	GetModuleFileName(hInstance, szPath, MAX_PATH);
	LPTSTR pSlash = _tcsrchr(szPath, '\\');
	if(pSlash)
	{
		*pSlash = 0;
	}
	PathAppend(szPath, _T("platform.ini"));

	if(!Load(szPath))
	{
		return FALSE;
	}
	
	m_hWnd = CreateWindowEx(WS_EX_APPWINDOW, szWindowClass, szWindowTitle, 
		WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU,
		m_nPosX, m_nPosY, m_nWidth, m_nHeight, NULL, NULL, hInstance, NULL);

	
	if(m_hWnd == NULL)
	{
		return FALSE;
	}
	RECT rWnd;
	GetWindowRect(m_hWnd, &rWnd);
	RECT rCln;
	GetClientRect(m_hWnd, &rCln);

	int cx = (rWnd.right-rWnd.left) - rCln.right;
	int cy = (rWnd.bottom-rWnd.top) - rCln.bottom;

	SetWindowPos(m_hWnd, NULL, m_nPosX, m_nPosY,
		cx+m_nWidth, cy+m_nHeight, SWP_DRAWFRAME|SWP_NOZORDER);

	OnCreate();

	
	// Show the main window
	ShowWindow(m_hWnd, nCmdShow);
	UpdateWindow(m_hWnd);
	
	return TRUE;
}

BOOL CApp::Load(LPCTSTR szIniFile)
{
	TCHAR szDllFile[MAX_PATH];
	memset(szDllFile, 0, sizeof(szDllFile));

	GetPrivateProfileString(_T("MODUL"),
		_T("LOAD_DLL"), NULL,
		szDllFile, MAX_PATH, szIniFile);

	if(_tcschr(szDllFile, _T(':')) == NULL)
	{
		TCHAR szPath[MAX_PATH];
		_tcscpy(szPath, szIniFile);

		LPTSTR pFile = PathFindFileName(szPath);
		if(pFile)
		{
			*pFile = 0;
			PathAppend(szPath, szDllFile);
			_tcscpy(szDllFile, szPath);
		}
	}

	m_nPosX = GetPrivateProfileInt(_T("MODUL"),
		_T("POS_X"), CW_USEDEFAULT, szIniFile);
	m_nPosY = GetPrivateProfileInt(_T("MODUL"),
		_T("POS_Y"), CW_USEDEFAULT, szIniFile);
	m_nWidth = GetPrivateProfileInt(_T("MODUL"),
		_T("WIDTH"), CW_USEDEFAULT, szIniFile);
	m_nHeight = GetPrivateProfileInt(_T("MODUL"),
		_T("HEIGHT"), CW_USEDEFAULT, szIniFile);

	GetPrivateProfileString(_T("GAME"),
		_T("FOLDER"), NULL, m_szFolder, MAX_PATH, szIniFile);

	if(_tcschr(m_szFolder, _T(':')) == NULL)
	{
		TCHAR szPath[MAX_PATH];
		_tcscpy(szPath, szIniFile);

		LPTSTR pFile = PathFindFileName(szPath);
		if(pFile)
		{
			*pFile = 0;
			PathAppend(szPath, m_szFolder);
			_tcscpy(m_szFolder, szPath);
		}
	}


	if(PathFileExists(szDllFile) && PathFileExists(m_szFolder))
	{
		m_hDll = LoadLibrary(szDllFile);
		if(m_hDll)
		{

			#define DLLAPI_OF(x) m_pf##x = (PF##x)\
						GetProcAddress(m_hDll, ""#x""); \
						if(m_pf##x == NULL) return FALSE

			DLLAPI_OF(GameInit);
			DLLAPI_OF(GameUpdate);
			DLLAPI_OF(GameRender);
			DLLAPI_OF(GameMessage);
			DLLAPI_OF(GameFree);

			return TRUE;
		}
	}
	return FALSE;
}

void CApp::ExitInstance()
{
	if(m_hDll)
	{
		m_pfGameUpdate = NULL;
		m_pfGameRender = NULL;
		m_pfGameMessage = NULL;
		FreeLibrary(m_hDll);
		m_hDll = NULL;
	}
}

//
// OnCommand()
// Handles WM_COMMAND messages
//
BOOL CApp::OnCommand(int nCmdID, int nEvent)
{
	switch (nCmdID)
	{				
	case IDM_EXIT:
		SendMessage(m_hWnd, WM_CLOSE, 0, 0);
		break;
	default:
		return FALSE;
	}
	return TRUE;
}

//
// Run()
// Starts the message pump
//
int CApp::Run()
{  
	const DWORD dwDelay = 100;
	
	MSG msg;

	// Main message loop:
	while (GetMessage(&msg, NULL, 0, 0)) 
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	
	HDC hDC = GetDC(m_hWnd);
	wglMakeCurrent(hDC, m_hRC);
	wglMakeCurrent(0, 0);
	wglDeleteContext(m_hRC);
	ReleaseDC(m_hWnd, hDC);
	m_hRC = NULL;
	
	return msg.wParam;
}

//
// WindowProc()
// Handles messages sent to the main window
//
LRESULT CApp::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT ps;
	HDC hDC;
	
	switch (message) 
	{
	case WM_CLOSE:
		DestroyWindow(m_hWnd);
		m_hWnd = NULL;
		break;
	case WM_COMMAND:
		if(!OnCommand(LOWORD(wParam), HIWORD(wParam)))
			return DefWindowProc(hWnd, message, wParam, lParam);
		break;
	case WM_PAINT:
		hDC = BeginPaint(hWnd, &ps);
		OnPaint(hDC);
		EndPaint(hWnd, &ps);
		break;
	case WM_CREATE:
		break;
	case WM_DESTROY:
		OnDestroy();
		PostQuitMessage(0);
		break;
	case WM_SIZE:
		OnSize(LOWORD(lParam), HIWORD(lParam));
		break;
	case WM_KEYDOWN:
		OnKeydown(wParam, lParam);
		break;
	case WM_CHAR:
		OnKeyChar(wParam, lParam);
		break;
	case WM_KEYUP:
		OnKeyup(wParam, lParam);
		break;
	case WM_MOUSEMOVE:
		OnMouseMove(wParam, lParam);
		break;
	case WM_LBUTTONDOWN:
		OnLButtonDown(wParam, lParam);
		break;
	case WM_LBUTTONUP:
		OnLButtonUp(wParam, lParam);
		break;
	case WM_TIMER:
		OnTimer(wParam, lParam);
		break;

	case WM_MOUSEWHEEL:
		OnMouseWheel(wParam, lParam);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

//
// OnIdle()
// Called when there are no messages in the message queue. 
// Sets current OpenGL RC and notifies the main window class that another frame 
// is to be drawn. 
//
void CApp::OnIdle()
{

}

//
// OnCreate()
// Called when the main window has been created. Initializes OpenGL.
//
void CApp::OnCreate()
{
	// Define pixel format
	PIXELFORMATDESCRIPTOR pfd;
	int nPixelFormat;	
	memset(&pfd, NULL, sizeof(pfd));    
	pfd.nSize      = sizeof(pfd);
	pfd.nVersion   = 1;
	pfd.dwFlags    = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
	pfd.iPixelType = PFD_TYPE_RGBA;
	pfd.cColorBits = 24;
	pfd.cDepthBits = 16;
	pfd.iLayerType = PFD_MAIN_PLANE;
	
	// Set pixel format
	HDC hDC = GetDC(m_hWnd); 
	nPixelFormat = ChoosePixelFormat(hDC, &pfd);
	SetPixelFormat(hDC, nPixelFormat, &pfd);
	
	// Create RC
	m_hRC = wglCreateContext(hDC);
	wglMakeCurrent(hDC, m_hRC);

	if(m_pfGameInit)
	{
		m_pfGameInit(m_szFolder, m_nWidth, m_nHeight);

		SetTimer(m_hWnd, 100, 100, NULL);
	}
	
	// Clean up
	ReleaseDC(m_hWnd, hDC);
}

//
// OnDestroy()
// Called when the main window is destroyed. Shuts down OpenGL
//
void CApp::OnDestroy()
{
	KillTimer(m_hWnd, 100);

	if(m_pfGameMessage)
	{
		m_pfGameMessage(MSG_QUIT, 0, 0);
	}
	if(m_pfGameFree)
	{
		m_pfGameFree();
	}
}

//
// OnSize()
// Called when the main window is resized. Resizes the OpenGL
// viewport.
//
void CApp::OnSize(int cx, int cy)
{
	if(cx==0 || cy ==0 || m_hWnd==NULL)
		return;

	// Size viewport
	RECT rc;
	GetClientRect(m_hWnd, &rc);
}

//
// OnPaint()
// Handles WM_PAINT messages. Redraws the OpenGL scene.
//
void CApp::OnPaint(HDC hDC)
{
	if(m_pfGameRender)
	{
		m_pfGameRender();
	}
	SwapBuffers(hDC);
}

void CApp::OnKeydown(WPARAM wParam, LPARAM lParam)
{
	if(m_pfGameMessage)
	{
		m_pfGameMessage(MSG_KEYDOWN, wParam, lParam);
	}
}

void CApp::OnKeyChar(WPARAM wParam, LPARAM lParam)
{
	if(m_pfGameMessage)
	{
		m_pfGameMessage(MSG_KEYCHAR, wParam, lParam);
	}
}

void CApp::OnKeyup(WPARAM wParam, LPARAM lParam)
{
	if(m_pfGameMessage)
	{
		m_pfGameMessage(MSG_KEYUP, wParam, lParam);
	}
}

void CApp::OnLButtonDown(WPARAM wParam, LPARAM lParam)
{
	if(m_pfGameMessage)
	{
		m_pfGameMessage(MSG_LBUTTONDOWN, wParam, lParam);
	}
}

void CApp::OnLButtonUp(WPARAM wParam, LPARAM lParam)
{
	if(m_pfGameMessage)
	{
		m_pfGameMessage(MSG_LBUTTONUP, wParam, lParam);
	}
}


void CApp::OnMouseMove(WPARAM wParam, LPARAM lParam)
{
	if(m_pfGameMessage)
	{
		m_pfGameMessage(MSG_MOUSEMOVE, wParam, lParam);
	}
}

void CApp::OnMouseWheel(WPARAM wParam, LPARAM lParam)
{
}

void CApp::OnTimer(WPARAM wParam, LPARAM lParam)
{
	if(m_pfGameUpdate)
	{
		if(m_pfGameUpdate(100) != 0)
		{
			// Quit Game
			OnDestroy();
		}
		InvalidateRect(m_hWnd, NULL, TRUE);
	}
	
}
